﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Nachos.Stats;


namespace Nachos.Interrupt
{
    public class InterruptController
    {
        private bool inHandler;

        public InterruptStatus InterruptStatus { get; set; }
        public MachineStatus MachineStatus { get; set; }

        void ChangeLevel(InterruptStatus old, InterruptStatus now)
        {
            InterruptStatus = now;
            //DEBUG(dbgInt, "\tinterrupts: " << intLevelNames[old] << " -> " << intLevelNames[now]);
        }
        bool CheckIfDue(bool advanceClock)
        {
            Interrupt next;
            Statistics stats = kernel.Statisticss;

            //ASSERT(level == IntOff);		// interrupts need to be disabled,
                            // to invoke an interrupt handler
            /*if (debug->IsEnabled(dbgInt)) 
            {
                DumpState();
            }*/

            if (pending.IsEmpty()) 
            {   	// no pending interrupts
                return false;	
            }		
            next = pending.First();
            if (next.When > stats.TotalTicks) 
            {
                if (!advanceClock) 
                {		
                    // not time yet
                    return false;
                }
                else 
                {      		// advance the clock to next interrupt
                    stats.IdleTicks += (next.When - stats.TotalTicks);
                    stats.TotalTicks = next.When;
                }
            }

            //DEBUG(dbgInt, "Invoking interrupt handler for the ");
            //DEBUG(dbgInt, intTypeNames[next->type] << " at time " << next->when);
            #ifdef USER_PROGRAM
            if (kernel.Machine != null) 
            {
                kernel.Machine.DelayedLoad(0, 0);
            }
            #endif
            inHandler = TRUE;
            do {
                next = pending.RemoveFirst();    // pull interrupt off list
                nex.CallOnInterrupt->CallBack();// call the interrupt handler
            } while (pending.Any() 
                    && (pending.First().When <= stats.TotalTicks));
            inHandler = false;
            return true;
        }
        
        public InterruptStatus SetLevel(InterruptStatus now)
        {
            InterruptStatus old = InterruptStatus;
    
            // interrupt handlers are prohibited from enabling interrupts
            //ASSERT((now == IntOff) || (inHandler == FALSE));

            ChangeLevel(old, now);			// change to new state
            if ((now == InterruptStatus.On) && (old == InterruptStatus.Off)) 
            {
                OneTick();				// advance simulated time
            }
            return old;
        }
        
        private void OneTick()
        {
            MachineStatus oldStatus = MachineStatus;
            Statistics stats = kernel.Statistics;

            // advance simulated time
            if (MachineStatus == MachineStatus.SystemMode) 
            {
                stats.TotalTicks += SystemTick;
                stats.SystemTicks += SystemTick;
            } 
            else 
            {					// USER_PROGRAM
                stats.TotalTicks += UserTick;
                stats.UserTicks += UserTick;
            }
            //DEBUG(dbgInt, "== Tick " << stats->totalTicks << " ==");

            // check any pending interrupts are now ready to fire
            ChangeLevel(InterruptStatus.On, InterruptStatus.Off);	// first, turn off interrupts
                        // (interrupt handlers run with
                        // interrupts disabled)
            CheckIfDue(FALSE);		// check for pending interrupts
            ChangeLevel(IntOff, IntOn);	// re-enable interrupts
            if (yieldOnReturn) {	// if the timer device handler asked 
                            // for a context switch, ok to do it now
            yieldOnReturn = FALSE;
            status = SystemMode;		// yield is a kernel routine
            kernel->currentThread->Yield();
            status = oldStatus;
            }
        }

        void Schedule(Interrupt callTo, int when)
        {

        }
    }
}
